package cn.green.core.executor;

import cn.green.core.components.exceptions.FieldValidateException;
import cn.green.core.components.field.FieldTypeCom;
import cn.green.core.components.store.field.FieldTypeStore;
import com.alibaba.fastjson.JSONObject;
import cn.green.core.components.database.DBRouter;
import cn.green.core.components.ddao.QDml;
import cn.green.core.components.entity.DatabaseSql;
import cn.green.core.components.field.DefaultTypeCom;
import cn.green.core.components.field.FieldAuthTypeCom;
import cn.green.core.components.field.FieldCheckTypeCom;
import cn.green.core.components.lock.TableLock;
import cn.green.core.components.setter.DeleteKey;
import cn.green.core.components.setter.PrimaryKey;
import cn.green.core.components.store.field.DefaultValueTypeStore;
import cn.green.core.components.store.field.FieldAuthTypeStore;
import cn.green.core.components.store.field.FieldCheckTypeStore;
import cn.green.core.components.store.table.TableTypeStore;
import cn.green.core.components.table.TableTypeCom;
import cn.green.core.model.QField;
import cn.green.core.model.QTable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wang.yishu
 * @version 1.0
 * @className DmlExecutor
 * @description dml执行器
 * @date 2024/11/26 13:57
 */
public class DmlExecutor {

    public DmlExecutor(QDml dml, DBRouter dbRouter, TableLock tableLock) {
        this.qDml =dml;
        this.dbRouter = dbRouter;
        this.tableLock = tableLock;
    }

    private final QDml qDml;
    private final DBRouter dbRouter;
    private final TableLock tableLock;

    public Object insertRecord(QTable table, JSONObject record) {
        if (table == null) {
            throw new RuntimeException("table is null");
        }
        if(table.getSourceName() == null || table.getSourceName().isEmpty()) {
            table.setSourceName(dbRouter.getMaster().getSourceName());
        }
        if (table.getSchemeName() == null || table.getSchemeName().isEmpty()) {
            table.setSchemeName(dbRouter.getMaster().getSchemeName());
        }
        tableLock.lock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        StringBuilder insertLog = new StringBuilder();
        try {
            String tableType = table.getTableType();
            if (tableType == null || tableType.isEmpty()) {
                throw new RuntimeException("QTable insert record error: table:[" +table.getTableName()+ "(" + table.getTableAliasName() + ")] table type is undefined.");
            }
            TableTypeCom tableTypeCom = TableTypeStore.get(tableType);
            QField primaryKeyField = tableTypeCom.getPrimaryKeyField();
            PrimaryKey primaryKey = tableTypeCom.getPrimaryKey();
            primaryKey.set(primaryKeyField, record);
            QField deleteKeyField = tableTypeCom.getDeleteKeyField();
            DeleteKey deleteKey = tableTypeCom.getDeleteKey();
            deleteKey.setNormal(deleteKeyField, record);
            List<QField> fields = table.getFields();
            for (QField field : fields) {
                if (field.getFieldName().equals(primaryKeyField.getFieldName()) || field.getFieldName().equals(deleteKeyField.getFieldName())
                        || (field.getFieldForm() != null && field.getFieldForm().equals(deleteKeyField.getFieldForm()))) {
                    continue;
                }
                //0.对数据进行解析
                String fieldType = field.getFieldType();
                if (fieldType != null && !fieldType.isEmpty()) {
                    FieldTypeCom fieldTypeCom = FieldTypeStore.get(fieldType);
                    fieldTypeCom.parseRecord(record, field);
                }
                field.setTableName(table.getTableName());
                Object value = record.get(field.getFieldName());
                //1.检查字段权限(是否有新增权限)
                String fieldAuth = field.getFieldAuth();
                if (fieldAuth != null && !fieldAuth.trim().isEmpty()) {
                    FieldAuthTypeCom fieldAuthTypeCom = FieldAuthTypeStore.get(fieldAuth);
                    if (!fieldAuthTypeCom.passInsert(field, value)) {
                        throw new RuntimeException("QTable insert record error: field:[" +field.getFieldName()+ "(" + field.getAliasName() + ")] has no insert authority.");
                    }
                }
                //2.设置默认值
                String defaultType = field.getDefaultType();
                if (defaultType != null && !defaultType.isEmpty()) {
                    DefaultTypeCom defaultTypeCom = DefaultValueTypeStore.get(defaultType);
                    if (defaultTypeCom != null) {
                        defaultTypeCom.set(field, record);
                    }
                }
                //3.检查校验类型
                List<String> fieldCheck = field.getFieldCheck();
                if (fieldCheck != null && !fieldCheck.isEmpty()) {
                    for (String fieldCheckItem : fieldCheck) {
                        FieldCheckTypeCom fieldCheckTypeCom = FieldCheckTypeStore.get(fieldCheckItem);
                        if (!fieldCheckTypeCom.check(primaryKeyField, field, record)) {
                            insertLog.append(field.getAliasName()).append(fieldCheckTypeCom.message());
                        }
                    }
                }
            }
            if (insertLog.length() > 0) {
                throw new FieldValidateException(insertLog.toString());
            }
            DatabaseSql databaseSql = this.dbRouter.get(table.getSourceName()).getDmlDialect().insertSql(table, record);
            databaseSql.setDatabase(this.dbRouter.get(table.getSourceName()));
            return this.qDml.insert(databaseSql);
        } finally {
            tableLock.unlock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        }
    }

    public Object updateRecord(QTable table, JSONObject record, String... conditions) {
        if (table == null) {
            throw new RuntimeException("table is null");
        }
        if(table.getSourceName() == null || table.getSourceName().isEmpty()) {
            table.setSourceName(dbRouter.getMaster().getSourceName());
        }
        if (table.getSchemeName() == null || table.getSchemeName().isEmpty()) {
            table.setSchemeName(dbRouter.getMaster().getSchemeName());
        }
        tableLock.lock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        try {
            String tableType = table.getTableType();
            if (tableType == null || tableType.isEmpty()) {
                throw new RuntimeException("QTable update record error: table:[" +table.getTableName()+ "(" + table.getTableAliasName() + ")] table type is undefined.");
            }
            TableTypeCom tableTypeCom = TableTypeStore.get(tableType);
            if (tableTypeCom == null) {
                throw new RuntimeException("QTable update record error: table:[" +table.getTableName()+ "(" + table.getTableAliasName() + ")] table type is not exists.");
            }
            QField primaryKeyField = tableTypeCom.getPrimaryKeyField();
            String primaryFieldName = primaryKeyField.getFieldName();
            QField deleteKeyField = tableTypeCom.getDeleteKeyField();
            String deleteKeyFieldName = deleteKeyField.getFieldName();
            Object primary = record.get(primaryFieldName);
            if (primary == null) {
                throw new RuntimeException("QTable update record error: table:[" +table.getTableName()+ "(" + table.getTableAliasName() + ")] primary value cannot be null.");
            }
            List<QField> fields = table.getFields();
            List<QField> updFields = new ArrayList<>();
            for (QField field : fields) {
                field.setTableName(table.getTableName());
                Object value = record.get(field.getFieldName());
                //1.检查字段权限(是否有修改权限)
                String fieldAuth = field.getFieldAuth();
                if (fieldAuth != null && !fieldAuth.trim().isEmpty()) {
                    FieldAuthTypeCom fieldAuthTypeCom = FieldAuthTypeStore.get(fieldAuth);
                    if (!fieldAuthTypeCom.passUpdate(field, value) || deleteKeyFieldName.equals(field.getFieldName()) || primaryFieldName.equals(field.getFieldName())) {
                        continue;
                    }
                }
                //2.设置默认值
                String defaultType = field.getDefaultType();
                if (defaultType != null && !defaultType.isEmpty()) {
                    DefaultTypeCom defaultTypeCom = DefaultValueTypeStore.get(defaultType);
                    defaultTypeCom.set(field, record);
                }
                //3.检查校验类型
                List<String> fieldCheck = field.getFieldCheck();
                if (fieldCheck != null && !fieldCheck.isEmpty()) {
                    for (String fieldCheckItem : fieldCheck) {
                        FieldCheckTypeCom fieldCheckTypeCom = FieldCheckTypeStore.get(fieldCheckItem);
                        if (!fieldCheckTypeCom.check(primaryKeyField, field, record)) {
                            throw new RuntimeException("QTable update record error: field:[" +field.getFieldName()+ "(" + field.getAliasName() + ")]: " + fieldCheckTypeCom.message());
                        }
                    }
                }
                updFields.add(field);
            }
            table.setFields(updFields);
            DatabaseSql databaseSql = this.dbRouter.get(table.getSourceName()).getDmlDialect().updateSql(table, record, conditions);
            databaseSql.setDatabase(this.dbRouter.get(table.getSourceName()));
            return this.qDml.update(databaseSql);
        } finally {
            tableLock.unlock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        }
    }

    public Object deleteRecord(QTable table, JSONObject param, String... conditions) {
        if (table == null) {
            throw new RuntimeException("table is null");
        }
        if(table.getSourceName() == null || table.getSourceName().isEmpty()) {
            table.setSourceName(dbRouter.getMaster().getSourceName());
        }
        if (table.getSchemeName() == null || table.getSchemeName().isEmpty()) {
            table.setSchemeName(dbRouter.getMaster().getSchemeName());
        }
        tableLock.lock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        try {
            DatabaseSql databaseSql = this.dbRouter.get(table.getSourceName()).getDmlDialect().deleteSql(table, param, conditions);
            databaseSql.setDatabase(this.dbRouter.get(table.getSourceName()));
            return this.qDml.delete(databaseSql);
        } finally {
            tableLock.unlock(table.getSourceName(), table.getSchemeName(), table.getTableName());
        }
    }
}
